using System;
using Introspector = Velocity4Net.Util.Introspection.Introspector;
using MethodInvocationException = Velocity4Net.Errors.MethodInvocationException;
using InternalContextAdapter = Velocity4Net.Ctx.InternalContextAdapter;
using Velocity4Net.App.Events;
using System.Text;
using Velocity4Net.Errors;
using Velocity4Net.Runtime.Logging;

namespace Velocity4Net.Runtime.Parse.AST
{
    /**
     * Returned the value of object property when executed.
     */
    public class PropertyExecutor : AbstractExecutor
    {
        private Introspector introspector;

        /**
         * @param log
         * @param introspector
         * @param clazz
         * @param property
         * @since 1.5
         */
        public PropertyExecutor(Log log, Introspector introspector,
                 Type clazz, String property)
        {
            this.log = log;
            this.introspector = introspector;

            // Don't allow passing in the empty string or null because
            // it will either fail with a StringIndexOutOfBounds error
            // or the introspector will get confused.
            if (!string.IsNullOrEmpty(property))
            {
                discover(clazz, property);
            }
        }


        /**
         * @return The current introspector.
         * @since 1.5
         */
        protected Introspector getIntrospector()
        {
            return this.introspector;
        }

        /**
         * @param clazz
         * @param property
         */
        protected void discover(Type clazz, String property)
        {
            /*
             *  this is gross and linear, but it keeps it straightforward.
             */

            try
            {
                Object[] _params = { };

                StringBuilder sb = new StringBuilder("get_");
                sb.Append(property);

                setMethod(introspector.getMethod(clazz, sb.ToString(), _params));

                if (!isAlive())
                {
                    /*
                     *  now the convenience, flip the 1st character
                     */

                    char c = sb[4];

                    if (Char.IsLower(c))
                    {
                        sb[4] = char.ToUpper(c);
                    }
                    else
                    {
                        sb[4] = char.ToLower(c);
                    }
                    setMethod(introspector.getMethod(clazz, sb.ToString(), _params));
                }
            }
            /**
             * pass through application level runtime exceptions
             */
            catch (ApplicationException e)
            {
                throw e;
            }
            catch (System.Exception e)
            {
                String msg = "Exception while looking for property getter for '" + property;
                log.error(msg, e);
                throw new VelocityException(msg, e);
            }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.AbstractExecutor#execute(java.lang.Object)
         */
        public override Object Execute(Object o)
        {
            return isAlive() ? getMethod().Invoke(o, ((Object[])null)) : null;
        }
    }
}
